package top.xlq.mydb.backend.tm;

import org.omg.PortableInterceptor.ACTIVE;
import top.xlq.mydb.backend.parser.Parser;
import top.xlq.mydb.backend.utils.Panic;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import top.xlq.mydb.common.Error;


public class TransactionManagerImpl implements TransactionManager {

    // XID 文件头长度
    static final int LEN_XID_HEADER_LENGTH = 8;
    // 每个事务的占用长度
    private static final int XID_FIELD_SIZE = 1;
    // 事务的三种状态
    private static final byte FIELD_TRAN_ACTIVE = 0;
    private static final byte FIELD_TRAN_COMMITTED = 1;
    private static final byte FIELD_TRAN_ABORTED = 2;
    // 超级事务，永远为 commited 状态
    public static final long SUPER_XID = 0;
    // XID 文件后缀
    static final String XID_SUFFIX = ".xid";

    private FileChannel fc;
    private RandomAccessFile file;
    private Long xidCounter;
    private Lock xidCounterLock;

    public TransactionManagerImpl(RandomAccessFile randomAccessFile, FileChannel fileChannel) {
        this.fc = fileChannel;
        this.file = randomAccessFile;
        xidCounterLock = new ReentrantLock();
        checkXIDCounter();

    }

    private void checkXIDCounter() {
        long fileLen = 0;
        try {
            // 获取文件的长度，RandomAccessFile在构造函数中赋值
            fileLen = file.length();
        } catch (IOException e) {
            // 如果出现异常，抛出BadXIDFileException错误
            Panic.panic(Error.BadXIDFileException);
        }
        // 如果文件长度小于XID头部长度，抛出BadXIDFileException错误
        if (fileLen < LEN_XID_HEADER_LENGTH) {
            Panic.panic(Error.BadXIDFileException);
        }
        // 分配一个长度为XID头部长度的ByteBuffer
        ByteBuffer buffer = ByteBuffer.allocate(LEN_XID_HEADER_LENGTH);
        try {
            // 将文件通道的位置设置为0
            fc.position(0);
            // 从文件通道读取数据到ByteBuffer
            fc.read(buffer);
        } catch (IOException e) {
            Panic.panic(Error.BadXIDFileException);
        }
        // 将ByteBuffer的内容解析为长整型，作为xidCounter
        this.xidCounter = Parser.parserLong(buffer.array());
        // 计算xidCounter+1对应的XID位置
        long end = getXidPosition(this.xidCounter + 1);
        // 如果计算出的XID位置与文件长度不符，抛出BadXIDFileException错误
        if (end != fileLen) {
            Panic.panic(Error.BadXIDFileException);
        }
    }

    // 根据事务xid取得其在xid文件中对应的位置
    private long getXidPosition(long xid) {
        return (xid - 1) * XID_FIELD_SIZE + LEN_XID_HEADER_LENGTH;
    }


    @Override
    public Long begin() {
        // 锁定计数器，防止并发问题
        xidCounterLock.lock();
        try {
            // xidCounter是当前事务的计数器，每开始一个新的事务，就将其加1
            // 调用updateXID方法，将新的事务ID和事务状态（这里是活动状态）写入到XID文件中
            long xid = xidCounter + 1;
            updateXid(xid, FIELD_TRAN_ACTIVE);
            // 调用incrXIDCounter方法，将事务计数器加1，并更新XID文件的头部信息
            incrXidCounter();
            return xid;
        } finally {
            xidCounterLock.unlock();
        }

    }

    private void incrXidCounter() {
        // 事务总数加一
        xidCounter++;
        // 将新的事务总数转换为字节数组，并用ByteBuffer包装
        ByteBuffer buffer = ByteBuffer.wrap(Parser.parserLong2Byte(xidCounter));
        try {
            fc.position(0);
            fc.write(buffer);
        } catch (IOException e) {
            Panic.panic(e);
        }
    }

    private void updateXid(Long xid, byte status) {
        // 获取事务xid在xid文件中对应的位置
        long offset = getXidPosition(xid);
        // 创建一个长度为XID_FIELD_SIZE的字节数组
        byte[] tmp = new byte[XID_FIELD_SIZE];
        // 将事务状态设置为status
        tmp[0] = status;
        // 使用字节数组创建一个ByteBuffer
        ByteBuffer buffer = ByteBuffer.wrap(tmp);
        try{
            // 将文件通道的位置设置为offset
            fc.position(offset);
            // 将ByteBuffer中的数据写入到文件通道
            fc.write(buffer);
        }catch (IOException ex){
            Panic.panic(ex);
        }
        try{
            // 强制将文件通道中的所有未写入的数据写入到磁盘
            fc.force(false);
        }catch (IOException ex){
            Panic.panic(ex);
        }
    }

    @Override
    public void commit(long xid) {
        updateXid(xid,FIELD_TRAN_COMMITTED);
    }

    @Override
    public void abort(long xid) {
        updateXid(xid,FIELD_TRAN_ABORTED);
    }

    @Override
    public boolean isActive(long xid) {
        if(xid == SUPER_XID) return false;
        return checkXid(xid,FIELD_TRAN_ACTIVE);
    }

    @Override
    public boolean isCommitted(long xid) {
        if(xid == SUPER_XID) return true;
        return checkXid(xid,FIELD_TRAN_COMMITTED);
    }

    @Override
    public boolean isAborted(long xid) {
        if(xid == SUPER_XID) return false;
        return checkXid(xid, FIELD_TRAN_ABORTED);
    }

    // 定义一个方法，接收一个事务ID（xid）和一个状态（status）作为参数
    private boolean checkXid(long xid, byte status){
        long xidPosition = getXidPosition(xid);
        ByteBuffer buffer = ByteBuffer.allocate(XID_FIELD_SIZE);
        try{
            fc.position(xidPosition);
            fc.read(buffer);
        }catch (IOException ex){
            Panic.panic(Error.BadXIDFileException);
        }
        // 检查字节缓冲区的第一个字节是否等于给定的状态
        // 如果等于，返回true，否则返回false
        return status == buffer.array()[0];
    }

    @Override
    public void close() {
        try {
            fc.close();
            file.close();
        } catch (IOException e) {
            Panic.panic(e);
        }
    }
}
